package com.cloud.xtools.common.validator;

import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import com.cloud.xtools.common.exception.ServiceException;
import com.cloud.xtools.common.mapper.DynamicSqlMapper;
import com.cloud.xtools.common.validator.annotation.FieldUnique;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.ConstraintValidator;
import jakarta.validation.ConstraintValidatorContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.HashMap;
import java.util.Map;

/**
 * 字段在数据库表中的唯一性验证器
 * 用法：在字段上添加注解 @FieldUnique(entity = User.class, field = "mobile", message = "当前手机号码已经被注册")
 * @author Daixinguo
 */
public class FieldUniqueValidator implements ConstraintValidator<FieldUnique,Object> {

    /**
     * 数据库表名
     */
    private String tableName;
    /**
     * 需要验证的字段名
     */
    private String fieldName;

    /**
     * 忽略的数据查询条件字段名
     */
    private String ignoreField;

    /**
     * 动态SQL查询器
     */
    @Autowired
    private DynamicSqlMapper dynamicSqlMapper;

    /**
     * 初始化验证器
     * 从注解中获取表名和字段名
     * @param constraintAnnotation 注解对象
     */
    @Override
    public void initialize(FieldUnique constraintAnnotation) {
        // 通过实体字节码（如：User.class）获取表名
        this.tableName = SqlHelper.table(constraintAnnotation.entity()).getTableName();
        this.fieldName = constraintAnnotation.field();
        // 需要排除的数据的查询条件字段，值从 request 中获取
        this.ignoreField = constraintAnnotation.ignoreField();
    }

    /**
     * 根据给定的值和字段名检查数据库中是否存在重复的记录
     * 此方法用于验证实体对象的某个字段值是否已经存在于数据库中，通常用于实现唯一性约束的验证
     *
     * @param value 待验证的字段值
     * @param context 验证器上下文，用于处理验证过程中的信息
     * @return 如果数据库中不存在相同的记录，则返回true，表示验证通过；否则返回false
     */
    @Override
    public boolean isValid(Object value, ConstraintValidatorContext context) {
        if(value == null || value.toString().isEmpty()){
            return true;
        }
        // 创建参数映射，将待验证的字段值放入参数映射中
        Map<String, Object> params = new HashMap<>();
        params.put(fieldName, value);
        // System.out.println(params);

        String sql = "";
        if(ignoreField.isEmpty()){
            // 不传入参数 ignoreField 时表示不需要排除任何数据
            // 构造SQL查询语句，用于检查数据库中是否存在相同的字段值
            sql = String.format("SELECT COUNT(*) FROM %s WHERE %s = #{params[%s]}", tableName, fieldName, fieldName);
            // System.out.println(sql);
        } else {
            // 获取当前请求对象
            // 获取的是当前线程绑定的请求属性对象 RequestAttributes。这个对象包含了与当前HTTP请求相关的所有信息，例如请求参数、会话信息、请求头等
            RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
            if (attributes == null) {
                // 为 null 跳过验证，通常正常的请求不回为空
                return true;
            }
            HttpServletRequest request = ((ServletRequestAttributes) attributes).getRequest();
            // 这里的 pathParameters 是在 RequestInterceptor 拦截器中设置的
            if(!(request.getAttribute("pathParameters") instanceof Map)){
                throw new ServiceException(fieldName + " 唯一性验证依赖 Path 参数：" + ignoreField);
            }
            Map<String, Object> pathParameters = (Map<String, Object>) request.getAttribute("pathParameters");
            // System.out.println(pathParameters);

            // 构造SQL查询语句，用于检查数据库中是否存在相同的字段值
            sql = String.format("SELECT COUNT(*) FROM %s WHERE %s = #{params[%s]} AND %s != #{params[%s]}", tableName, fieldName, fieldName, ignoreField, ignoreField);

            // 将 pathParameters 中对应 ignoreField 字段的值放入参数映射中
            params.put(ignoreField, pathParameters.get(ignoreField));
            // System.out.println(sql);
            // System.out.println(params);
        }

        // 执行SQL查询，获取数据库中匹配记录的数量
        Long count = (Long) dynamicSqlMapper.selectBySql(sql, params);
        // System.out.println(count);
        return count == 0;
    }
}
